package edu.hawaii.ics211.exam;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingWorker;

/**
 * The panel containing the exam.
 * Provides users with the ability to move forward and backward between questions.
 * 
 * @author Branden Ogata
 *
 */

@SuppressWarnings("serial")
public class ExamViewer extends JPanel
{
  /**
   * The list of questions.
   * 
   */
  private List<ExamQuestion> questions;
  
  /**
   * The index of the current question.
   * 
   */
  private int currentQuestion;
  
  /**
   * The footer for the panel.
   * Contains the navigation buttons.
   * 
   */
  private JPanel footer;
  
  /**
   * The View Question button.
   * 
   */
  private JButton viewQuestionButton;
  
  /**
   * The View Question field.
   * 
   */
  private JTextField viewQuestionField;
  
  /**
   * The Previous button.
   * 
   */
  private JButton previousButton;
  
  /**
   * The List Questions button.
   * 
   */
  private JButton listQuestionsButton;
  
  /**
   * The Next button.
   * 
   */
  private JButton nextButton;
  
  /**
   * The End Exam button.
   * 
   */
  private JButton endExamButton;
  
  /**
   * Initializes the list of questions and sets up the panel.
   * 
   * @param questions The List<ExamQuestion> containing the questions for the exam.
   * 
   */
  
  public ExamViewer(List<ExamQuestion> questions)
  {
    // Sets up the panel
    this.setLayout(new BorderLayout());

    // Sets up the list of questions
    this.questions = new ArrayList<ExamQuestion>(questions);
    this.currentQuestion = 0;
    
    // The first question should start off in the center
    this.add(this.questions.get(this.currentQuestion), BorderLayout.CENTER);
    
    // Sets up the footer
    this.footer = new JPanel();
    this.footer.setLayout(new BoxLayout(this.footer, BoxLayout.LINE_AXIS));
    ButtonListener listener = new ButtonListener();
    
    this.viewQuestionButton = new JButton("View Question #");
    this.viewQuestionButton.addActionListener(listener);
    this.viewQuestionButton.setFont(new Font(Font.SERIF, 
                                             this.viewQuestionButton.getFont().getStyle(),
                                             this.viewQuestionButton.getFont().getSize()));

    this.footer.add(this.viewQuestionButton);
    
    this.viewQuestionField = new JTextField(4);
    this.viewQuestionField.setText("0");
    this.viewQuestionField.setFont(new Font(Font.SERIF, 
                                            this.viewQuestionField.getFont().getStyle(),
                                            this.viewQuestionField.getFont().getSize()));
    this.viewQuestionField.setMaximumSize(this.viewQuestionButton.getPreferredSize());
    this.footer.add(this.viewQuestionField);
    
    this.footer.add(Box.createHorizontalGlue());
    
    this.previousButton = new JButton("Previous");
    this.previousButton.addActionListener(listener);
    this.previousButton.setFont(new Font(Font.SERIF, this.previousButton.getFont().getStyle(),
                                                     this.previousButton.getFont().getSize()));
    this.previousButton.setMaximumSize(this.previousButton.getPreferredSize());
    this.previousButton.setEnabled(false);
    this.footer.add(this.previousButton);
    
    this.listQuestionsButton = new JButton("List Questions");
    this.listQuestionsButton.addActionListener(listener);
    this.listQuestionsButton.setFont(new Font(Font.SERIF, 
                                              this.listQuestionsButton.getFont().getStyle(),
                                              this.listQuestionsButton.getFont().getSize()));
    this.listQuestionsButton.setMaximumSize(this.listQuestionsButton.getPreferredSize());
    this.footer.add(this.listQuestionsButton);

    this.nextButton = new JButton("Next");
    this.nextButton.addActionListener(listener);
    this.nextButton.setFont(new Font(Font.SERIF, this.nextButton.getFont().getStyle(),
                                                 this.nextButton.getFont().getSize()));
    this.nextButton.setMaximumSize(this.previousButton.getPreferredSize());
    
    if (this.questions.size() <= 1)
    {
      this.nextButton.setEnabled(false);
    }
    
    this.footer.add(this.nextButton);

    this.footer.add(Box.createHorizontalGlue());
    
    this.endExamButton = new JButton("End Exam");
    this.endExamButton.addActionListener(listener);
    this.endExamButton.setFont(new Font(Font.SERIF, this.endExamButton.getFont().getStyle(),
                                                    this.endExamButton.getFont().getSize()));
    this.footer.add(this.endExamButton);
    
    this.add(this.footer, BorderLayout.SOUTH);
  }
  
  /**
   * Listens for any button presses.
   * 
   */
  
  private class ButtonListener implements ActionListener
  {
    /**
     * Handles any button presses that do occur.
     *
     * @param event The ActionEvent that recorded the button press.
     * 
     */
    
    @Override
    public void actionPerformed(ActionEvent event)
    {
      // If the source was the View Question button
      if (event.getSource() == viewQuestionButton)
      {
        // Verify that the question number provided was valid
        String questionText = viewQuestionField.getText();
        
        // If not an integer, then reset to the question number
        if (!questionText.matches("\\d+"))
        {
          viewQuestionField.setText(String.valueOf(currentQuestion));
        }
        else
        {
          int questionNumber = Integer.valueOf(viewQuestionField.getText());
          
          // If less than 0, go to 0
          if (questionNumber < 0)
          {
            questionNumber = 0;
          }
          // Else if greater than the last question, go to the last question
          else if (questions.size() <= questionNumber)
          {
            questionNumber = questions.size() - 1;
          }

          // Replace the center panel with the question indicated in the View Question field
          updateQuestion(questionNumber);
        }
      }
      
      // Else if the Previous button was pressed
      else if (event.getSource() == previousButton)
      {
        updateQuestion(currentQuestion - 1);
      }
      
      // Else if the List Questions button was pressed
      else if (event.getSource() == listQuestionsButton)
      {
        // Show a non-modal dialog listing all of the questions on the exam
        JDialog questionList = new JDialog();
        questionList.setTitle("Exam Questions");
        questionList.setModal(false);
        questionList.setLayout(new GridLayout(0, 1));
        
        double tallest = 0.0;
        
        // Add each question to the list
        for (ExamQuestion question : questions)
        {
          JLabel current = new JLabel("<html>" + question.getQuestion() + "</html>");
          questionList.add(current);
          
          if (current.getPreferredSize().getHeight() > tallest)
          {
            tallest = current.getPreferredSize().getHeight();
          }
        }
        
        questionList.pack();
        questionList.setMinimumSize(new Dimension((int) questionList.getMinimumSize().getWidth(),
                                                  (int) (tallest * questions.size())));
        questionList.setPreferredSize(new Dimension((int) questionList.getMinimumSize().getWidth(),
                                                    (int) (tallest * questions.size())));
        questionList.setVisible(true);
      }
      
      // Else if the Next button was pressed
      else if (event.getSource() == nextButton)
      {
        updateQuestion(currentQuestion + 1);
      }
      
      // Else if the End Exam button was pressed and the user confirms the decision to end the exam
      else if ((event.getSource() == endExamButton) && 
               (JOptionPane.showConfirmDialog(null, "Do you want to end the exam?", "Confirm Exit", 
                                              JOptionPane.OK_CANCEL_OPTION) == 
                                              JOptionPane.OK_OPTION))
      {
        final GradingTask task = new GradingTask(questions);
        task.execute();
        
        task.addPropertyChangeListener(new PropertyChangeListener()
        {
          /**
           * Handles the progression of the grading task.
           * 
           * @param changeEvent The PropertyChangeEvent that recorded the change.
           * 
           */
          
          @Override
          public void propertyChange(PropertyChangeEvent changeEvent)
          {
            if (("state".equals(changeEvent.getPropertyName())) && 
                ("DONE".equals(changeEvent.getNewValue().toString())))
            {
              try
              {
                // Display the results in this panel
                ExamViewer.this.removeAll();
                JPanel resultsPanel = new JPanel();
                JTextArea reviewTextArea = new JTextArea(32, 64);
                reviewTextArea.setText(task.get());
                JScrollPane reviewPane = new JScrollPane(reviewTextArea);
                reviewPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
                reviewTextArea.setLineWrap(true);
                reviewTextArea.setWrapStyleWord(true);
                reviewTextArea.setEditable(false);
                resultsPanel.add(reviewPane);
                
                ExamViewer.this.add(resultsPanel, BorderLayout.CENTER);
                ExamViewer.this.revalidate();
              }
              catch (InterruptedException e)
              {
                // TODO Auto-generated catch block
                e.printStackTrace();
              }
              catch (ExecutionException e)
              {
                // TODO Auto-generated catch block
                e.printStackTrace();
              }
            }
          }
          
        });
      }
    }
    
    /**
     * Update the center area of the panel with a new question.
     * 
     * @param questionNumber The int equal to the index of the next question.
     * 
     */
    
    private void updateQuestion(int questionNumber)
    {
      ExamViewer.this.remove(questions.get(currentQuestion));
      questions.get(currentQuestion).setVisible(false);
      ExamViewer.this.add(questions.get(questionNumber), BorderLayout.CENTER);
      questions.get(questionNumber).setVisible(true);
      viewQuestionField.setText(String.valueOf(questionNumber));
      currentQuestion = questionNumber;
      ExamViewer.this.revalidate();
      
      // If this is the first question, then disable the Previous button
      previousButton.setEnabled((currentQuestion == 0) ? (false) : (true));
      
      // If this is the last question, then disable the Next button
      nextButton.setEnabled((currentQuestion == questions.size() - 1) ? (false) : (true));
    }    
    
    /**
     * Displays a progress bar to show grading progress after exam submission.
     * 
     */
    
    private class GradingTask extends SwingWorker<String, Void>
    {
      /**
       * The questions and answers to grade.
       * 
       */
      private List<ExamQuestion> questions;
      
      /**
       * Initializes fields for a new GradingTask.
       * 
       * @param questions The List<ExamQuestion> containing questions and answers to grade.
       * 
       */
      
      public GradingTask(List<ExamQuestion> questions)
      {
        this.questions = new ArrayList<ExamQuestion>(questions);
      }
      
      /**
       * Grades the questions.
       * 
       * @return results The String containing text output from the grading process.
       * 
       * @throws Exception if any issues occur during the grading process.
       * 
       */
      
      @Override
      protected String doInBackground() throws Exception
      {
        String results = "";
        double examPoints = 0;
        CheckAnswer.resetPoints();
        int questionsGraded = 0;
        
        // Set up the progress bar
        JPanel progressPanel = new JPanel();
        progressPanel.setLayout(new BorderLayout());
        JProgressBar progressBar = new JProgressBar();
        progressBar.setStringPainted(true);
        progressPanel.add(progressBar, BorderLayout.CENTER);
        JDialog progressDialog = new JDialog();
        progressDialog.setTitle(String.format("Grading question (%d/%d)", 
                                              questionsGraded, 
                                              this.questions.size()));
        progressDialog.setModal(false);
        progressDialog.add(progressPanel);
        progressDialog.pack();
        progressDialog.setSize(((int) progressDialog.getPreferredSize().getWidth() * 2),
                               (int) progressDialog.getPreferredSize().getHeight());
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        progressDialog.setLocation((int) (screenSize.getWidth() / 2), 
                                   (int) (screenSize.getHeight() / 2));
        progressDialog.setVisible(true);        
        
        // Append the results for each question
        for (ExamQuestion question : questions)
        {
          String result = CheckAnswer.checkAnswer(question.getAnswer().getText(), 
                                                  question.getTestPath(), question.getPoints());
          /*
          System.out.println(result);
          */
          results += question.getQuestion() + "\n" + result + "\n\n";
          examPoints += question.getPoints();
          
          questionsGraded++;
          progressDialog.setTitle(String.format("Grading question (%d/%d)", 
                                                questionsGraded, 
                                                this.questions.size()));
          progressBar.setValue((int) ((((double) questionsGraded) * 100) / 
                                      ((double) this.questions.size())));
          progressBar.repaint();
        }
          
        results += String.format("Overall Score: %.2f/%.2f%n", CheckAnswer.getTotalPoints(),
                                                               examPoints);
          
        progressDialog.setVisible(false);
        return results;
      } 
    }
  }
}